#include "Lock.hpp"
#include "Log.hpp"
#include <queue>
#include <string>
#include <cstdlib>
#include <cassert>
#include <pthread.h>
#include <memory>
#include <iostream>
using namespace std;
#include <unistd.h>

int gThreadNum = 5;
template <class T>
class ThreadPool
{
public:
    static ThreadPool<T> *getInstance()
    {
        static Mutex mutex;
        if (instance == nullptr)
        {
            LockGuard lockGuard(&mutex);
            if (instance == nullptr)
            {
                instance = new ThreadPool<T>();
            }
        }
        return instance;
    }
    void push(const T &in)
    {
        lockQueue();
        taskQueue_.push(in);
        choiceThreadForHander();
        unlockQueue();
        // sleep(1);
    }
    static void *threadRoutine(void *args)
    {
        pthread_detach(pthread_self());
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
        while (true)
        {
            tp->lockQueue();
            while (!tp->haveTask())
            {
                tp->waitForTask();
            }
            T t = tp->pop();
            tp->unlockQueue();
            t();
        }
    }
    void start()
    {
        assert(!isStart_); //防止线程池被重复启动
        for (int i = 0; i < threadNum_; i++)
        {
            pthread_t temp;
            pthread_create(&temp, nullptr, threadRoutine, (void *)this);
        }
        isStart_ = true;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }
private:
    ThreadPool(const ThreadPool<T> &) = delete;
    void operator=(const ThreadPool<T> &) = delete;
    ThreadPool(int threadNum = gThreadNum) : threadNum_(threadNum), isStart_(false)
    {
        assert(threadNum_ > 0);
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }

    T pop()
    {
        T temp = taskQueue_.front();
        taskQueue_.pop();
        return temp;
    }
    bool haveTask()
    {
        return !taskQueue_.empty();
    }
    void waitForTask()
    {
        pthread_cond_wait(&cond_, &mutex_);
    }
    void lockQueue()
    {
        pthread_mutex_lock(&mutex_);
    }
    void unlockQueue()
    {
        pthread_mutex_unlock(&mutex_);
    }
    void choiceThreadForHander()
    {
        pthread_cond_signal(&cond_);
    }

private:
    bool isStart_;
    int threadNum_;
    queue<T> taskQueue_;
    pthread_mutex_t mutex_;
    pthread_cond_t cond_;
    static ThreadPool<T> *instance;
};

template<class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;